package utils

import (
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"reflect"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

// MarshalCommand marshals the given command.
func ProtobufMarshalCommand(msg proto.Message) ([]byte, error) {
	b, err := proto.Marshal(msg)
	return b, err
}

func JsonMarshalCommand(msg proto.Message) ([]byte, error) {
	m := &jsonpb.Marshaler{
		EmitDefaults: true,
	}
	str, err := m.MarshalToString(msg)
	b := []byte(str)
	return b, err
}

func ShellExec(command string) error {
	cmd := exec.Command("sh", "-c", command)

	stdin, _ := cmd.StdinPipe()
	stdout, _ := cmd.StdoutPipe()
	stdin.Close()
	stdout.Close()

	if err := cmd.Start(); err != nil {
		return errors.Wrap(err, "fail to start")
	}

	data, _ := ioutil.ReadAll(stdout)
	if err := cmd.Wait(); err != nil {
		return errors.Wrap(err, "fail to wait")
	}

	log.Info(string(data))
	return nil
}

func IsFileExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func DeepCopy(value interface{}) interface{} {
	if valueMap, ok := value.(map[string]interface{}); ok {
		newMap := make(map[string]interface{})
		for k, v := range valueMap {
			newMap[k] = DeepCopy(v)
		}

		return newMap
	} else if valueSlice, ok := value.([]interface{}); ok {
		newSlice := make([]interface{}, len(valueSlice))
		for k, v := range valueSlice {
			newSlice[k] = DeepCopy(v)
		}

		return newSlice
	}

	return value
}

func StructCopy(from, to interface{}) {
	fromValue := reflect.ValueOf(from)
	toValue := reflect.ValueOf(to)
	// 必须是指针类型
	if fromValue.Kind() != reflect.Ptr || toValue.Kind() != reflect.Ptr {
		return
	}
	if fromValue.IsNil() || toValue.IsNil() {
		return
	}
	// 获取到来源数据
	fromElem := fromValue.Elem()
	// 需要的数据
	toElem := toValue.Elem()
	for i := 0; i < toElem.NumField(); i++ {
		toField := toElem.Type().Field(i)
		// 看看来源的结构体中是否有这个属性
		fromFieldName, ok := fromElem.Type().FieldByName(toField.Name)
		// 存在相同的属性名称并且类型一致
		// TODO 可以根据需要判断是否是空值
		if ok && fromFieldName.Type == toField.Type {
			toElem.Field(i).Set(fromElem.FieldByName(toField.Name))
		}
	}
}

// https://blog.csdn.net/ycf8788/article/details/83111080
func GetLocalIp() (string, error) {
	netInterfaces, err := net.Interfaces()
	if err != nil {
		return "localhost", errors.Wrap(err, "fail to get interfaces")
	}

	for i := 0; i < len(netInterfaces); i++ {
		if (netInterfaces[i].Flags & net.FlagUp) != 0 {
			addrs, _ := netInterfaces[i].Addrs()

			for _, address := range addrs {
				if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
					if ipnet.IP.To4() != nil {
						return ipnet.IP.String(), nil
					}
				}
			}
		}
	}

	return "localhost", errors.Wrap(err, "not found")
}
